Skip to content

fix(formula): give the test-typecheck program the node typings its two fixture-loading tests already assumed - #16902

Merged
os-musk merged 1 commit into
mainfrom
claude/issue-16297-formula-test-types-node
Sep 8, 2026
Merged

fix(formula): give the test-typecheck program the node typings its two fixture-loading tests already assumed#16902
os-musk merged 1 commit into
mainfrom
claude/issue-16297-formula-test-types-node

Conversation

@os-musk

@os-musk os-musk commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16297

packages/formula declared no @types/node and its test program named no types, so node:fs / node:path / node:url and import.meta.url never resolved inside tsconfig.test.json. This adds the devDependency and the types entry and re-records the shrink-only ledger. No test file is edited.

Clause-②: no

The ledger reading, before and after

Both quoted whole, from pnpm --filter @objectstack/formula check:test-typecheck on this branch.

BEFORE (at origin/main = eabdd66f45, dependency closure built, exit 0):

check:test-typecheck: OK — @objectstack/formula's test layer compiles under packages/formula/tsconfig.test.json; 5 file(s) / 15 error(s) / 12 pinned signature(s) held in test-typecheck-debt.json (shrink-only and identity-pinned, https://github.com/objectstack-ai/objectstack/issues/5286).

AFTER (exit 0):

check:test-typecheck: OK — @objectstack/formula's test layer compiles under packages/formula/tsconfig.test.json; 3 file(s) / 7 error(s) / 4 pinned signature(s) held in test-typecheck-debt.json (shrink-only and identity-pinned, https://github.com/objectstack-ai/objectstack/issues/5286).

Delta: 15 -> 7 errors, 5 -> 3 files, 12 -> 4 signatures. Every move is downward; the ledger is shrink-only and nothing was added to it.

The intermediate state is the sharpest evidence of which errors these were. With the fix applied and the ledger not yet regenerated, the gate exits 1 and names exactly two files:

check:test-typecheck: 2 problem(s)

  • src/rls-predicate.test.ts: ledger records 4 type error(s) but tsc reports none — it GRADUATED, or the file moved/vanished. Delete its entry from test-typecheck-debt.json in the same change.
  • src/skill-catalog-sync.test.ts: ledger records 4 type error(s) but tsc reports none — it GRADUATED, or the file moved/vanished. Delete its entry from test-typecheck-debt.json in the same change.

Coverage did not move to make the number look better: tsc --noEmit --listFiles -p tsconfig.test.json still puts 29 of 29 src/**/*.test.ts in the program after the change.

⚠️ The card's headline count is refuted: it is 8 of 15, not 15 of 15

#16297 is titled "15 of its test-typecheck ledger errors have one cause". Measured on origin/main, the node-typings cause is 8 of the 15, and the repo had already recorded that twice before the card existed.

packages/formula/tsconfig.test.json's own header, written by the PR that seeded this ledger:

⚠️ A COMPOSITION CORRECTION the deleted ledger entry earned: it attributed its TS2591 x6 to «process». Measured here, all six are node builtins named as bare module specifiers — node:fs x2, node:path x2, node:url x2, in those same two files — and the TS2339 x2 beside them read Property 'url' does not exist on type 'ImportMeta'. Both classes SURVIVE this move, and both have one cause: this package declares no @types/node and its tsconfig names no types, so the node typings never reach its program. That is real debt with a real repair. ⛔ It is deliberately NOT repaired here — seeding the ledger is the deliverable, and turning an onboarding into a cleanup is how it stops landing at all.

scripts/check-type-check-coverage.mjs carries the same correction independently, and both say 6 + 2 = 8. The full enumeration of the 15 on origin/main, from tsc --noEmit --pretty false -p tsconfig.test.json:

file class count this cause?
src/rls-predicate.test.ts TS2591 node:fs / node:path / node:url 3 yes
src/rls-predicate.test.ts TS2339 Property 'url' does not exist on type 'ImportMeta' 1 yes
src/skill-catalog-sync.test.ts TS2591 node:fs / node:url / node:path 3 yes
src/skill-catalog-sync.test.ts TS2339 Property 'url' does not exist on type 'ImportMeta' 1 yes
src/cel-to-filter.test.ts TS2739 missing properties on a fixture literal 1 no
src/matches-filter-temporal-conformance.test.ts TS2345 TemporalRow / TemporalTimeRow not assignable 3 no
src/normalize.test.ts TS2352 cast does not sufficiently overlap 3 no

8 + 7 = 15. The triage comment on the card anticipated exactly this and pre-ruled it: "若改完配置数字只掉了一部分,剩下的不是这张卡的,请另报。" The surviving 7 are three unrelated classes in three other files; they are recorded in the ledger's authored _note and are not filed as a card here — see the acceptance notes below.

Where the types entry goes, and why not the build config

The card said to read the sibling's header first. It answers the question in its opening sentence:

tsconfig.json beside this file stays exactly as it is: it is the BUILD config, and its **/*.test.ts exclusion has a reason — ci.yml gates that no test file reaches the published artifact.

So types goes on the test program. That the build is genuinely untouched is measured rather than asserted: packages/formula/dist was hashed before and after (sha256, all 6 emitted files, diff exit 0) and is byte-identical.

Which of the two lines actually does the work was measured in both directions on a fully built closure, because the answer is not the obvious one:

  • types: ["node"] with no package-local devDependency at all already reports 7 — the entry alone does the whole job, resolving @types/node off the root hoist.
  • the devDependency without the entry reports 15, unchanged. Adding @types/node to package.json on its own would have been a no-op that looked like a repair.

The devDependency earns its line by making the dependency declared instead of borrowed, and the pair is what packages/spec (#5286), packages/verify, packages/cli and packages/drivers/driver-mongodb all carry.

Proving the pin can still fail

The ledger only moved down, so the ledger alone cannot show the gate still works. Ablated on the committed tree (56067ee055), removing both lines the repair added, under a trap … EXIT INT TERM with absolute paths:

-- anchor BEFORE: compilerOptions types line = 1, devDep line = 1
-- anchor AFTER (both must be 0): types line = 0, devDep line = 0
-- mutation is on disk (git diff HEAD --stat):
 packages/formula/package.json       | 1 -
 packages/formula/tsconfig.test.json | 3 +--
-- ABLATED GATE EXIT = 1  (expected non-zero)
check:test-typecheck: 2 problem(s)

  • src/rls-predicate.test.ts: 4 type error(s) in a file the ledger does not cover. ...
  • src/skill-catalog-sync.test.ts: 4 type error(s) in a file the ledger does not cover. ...

Red for exactly the right reason: once the two files GRADUATED, the ledger stops covering them, so the 8 errors are red on arrival rather than absorbed by a number.

Restore is proved, not assumed:

-- blob hashes now: tsconfig.test.json=e06caaf7a4e19a0bf7b5c51ca613a424fc34f2c8 package.json=20d659f2b0ed2cdd8d6630e45fe1842a98304f9f
-- blob-hash equality with HEAD: OK
-- whole-tree git status --porcelain (must be empty):
-- (end of status)
RESTORE PROVED: blob-hash equality AND empty whole-tree status.
-- RESTORED GATE EXIT = 0 (expected 0)

Verification

All exit codes captured by redirect-then-$?, never across a pipe.

command exit
pnpm --filter @objectstack/formula test 0 — 29 test files, 827 tests passed
pnpm --filter @objectstack/formula typecheck 0 — both legs (tsc --noEmit + check:test-typecheck)
pnpm --filter @objectstack/formula check:test-typecheck 0 — the AFTER reading above
node scripts/check-type-check-coverage.mjs --re-measure 0 — the card's second criterion
pnpm lint (eslint . --no-inline-config, whole repo) 0 at 56067ee055
49 derived gate families 0 — all 49, tallied from a per-command exit log

check:type-check-coverage --re-measure stayed green, which is the card's guard against making the number look better by moving coverage:

check-type-check-coverage --re-measure: OK — 5 ledger entr(ies) re-measured in 360.6s, 55 raw tsc error(s) total, none above its recorded number.
  surplus: none — every entry sits exactly at its measurement, so any new error is red.

Gates derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, run, then reconciled. The reconciliation line, verbatim:

✓ dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.

⚠️ That line is COVERAGE, not verdicts. The verdicts are stated separately above: all 49 exited 0.

Three things worth flagging about the measurement itself rather than burying:

  • pnpm check:dual-build-cjs-loads first returned exit 3 — PREREQUISITE NOT MET, which is NOT MEASURED and not a pass. It was re-run to a real verdict (0) after pnpm build (73/73 tasks). packages/formula/dist was re-hashed after that full rebuild and is still byte-identical to the pre-change baseline.
  • The first run of the which-line probe was taken while that workspace build was mid-flight and read a half-written packages/spec/dist; it reported 57 errors, of which 45 were TS7016 Could not find a declaration file for module '@objectstack/spec'. That reading is void and is disclosed rather than quietly re-run. Re-taken on the settled tree it reports 15.
  • The gate derivation warns the tree is 3 commits behind origin/main (625b0c32b4) and that one family-defining file, scripts/check-react-page-adapter-contract.mjs, changed in that range. The derived command set was byte-identical across derivations before and after a fresh git fetch, and that script is neither in the 49 nor reads any of this PR's 4 paths.

Changeset route: the skip-changeset label

AGENTS.md:1036 — measured on this branch at eabdd66f45, which is origin/main's content; the same sentence sits at a different line number on other trees, so the line number alone is not a citation:

A bug fix in a released package takes a patch changeset — never none, and ⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package.

Measured against that test, this diff publishes nothing. npm pack --dry-run --json in packages/formula reports a 10-file tarball; of the four paths this PR touches:

  • packages/formula/tsconfig.test.jsonnot in the tarball
  • packages/formula/test-typecheck-debt.jsonnot in the tarball
  • pnpm-lock.yaml — repo root, not in the tarball
  • packages/formula/package.json — in the tarball, and its only delta is one devDependencies line, which no consumer installs

and all 6 dist/ files are byte-identical. So: the skip-changeset label, never an empty changeset.

Clause-②: no holds on the same evidence — no new exported symbol and no new key on any published payload, with dist/ byte-identical proving it rather than asserting it.

验收备注

  • The surviving 7 errors are noted, not filed. They are already recorded, visible and gated — they sit in packages/formula/test-typecheck-debt.json under the shrink-only ratchet, and PR chore(typecheck): seven ledgered packages type-check their own tests through a sibling test program (#12511) #16295 / [finding] A type error in a ledgered package tests is invisible to its own typecheck and discoverable only by a full-closure build — 8 packages in that state (was 19; re-measured 2026-09-05) #12511 already track the residue that produced this card. A new card would duplicate that. Their three classes are named in the ledger's _note so the next reader does not have to re-derive them.
  • scripts/check-type-check-coverage.mjs carries a historical paragraph whose present-tense clause ("this package declares no @types/node and its tsconfig names no types") this PR falsifies. It is left untouched deliberately: the paragraph is explicitly anchored to a measurement at 6a1e38244, formula no longer has a TEST_DEBT entry there, and editing that file would put its own self-test and prose pins in this diff for no gain. Flagged for the maintainer rather than swept in.
  • Census of the same cause elsewhere, reported and not swept in. Across the 39 packages with a tsconfig.test.json, three declare no types in their test program while their test layer does import node builtins — @objectstack/core (5 files), @objectstack/platform-objects (1), @objectstack/plugin-email (1). All three already declare @types/node as a devDependency and all three carry zero ledger errors of this cause, so the cause is not currently biting them. No card filed.

Generated by Claude Code

…o fixture-loading tests already assumed

`packages/formula` declared no `@types/node` and its test program named no
`types`, so `node:fs` / `node:path` / `node:url` and `import.meta.url` never
resolved in `tsconfig.test.json`. Adds the devDependency and the `types` entry,
and re-records the shrink-only ledger.

Ledger, measured on this branch with NO test file edited:
  before  5 file(s) / 15 error(s) / 12 pinned signature(s)
  after   3 file(s) /  7 error(s) /  4 pinned signature(s)

The card's title generalised the cause to all 15; measured, it is 8 of the 15 —
the whole of src/rls-predicate.test.ts and src/skill-catalog-sync.test.ts, which
both GRADUATED. The surviving 7 are three unrelated classes in three other files
and are not this cause.

`tsconfig.json` — the BUILD config — is untouched, per its sibling's recorded
rationale, and `packages/formula/dist` is byte-identical across the change
(6 files, per-file sha256).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
@github-actions github-actions Bot added the size/s label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/formula/test-typecheck-debt.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/formula/test-typecheck-debt.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 44c849c7d66977ed28814f15853d3e9f984275b7packageMentionDocs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants